package com.gitee.minimalismstyle.fresh.common.api.mvc

import com.gitee.minimalismstyle.fresh.common.api.annotation.ControllerFeignClient
import org.springframework.core.annotation.AnnotatedElementUtils
import org.springframework.lang.Nullable
import org.springframework.util.ClassUtils
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.servlet.mvc.condition.RequestCondition
import org.springframework.web.servlet.mvc.method.RequestMappingInfo
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
import java.lang.reflect.AnnotatedElement
import java.lang.reflect.Method

class ControllerFeignClientHandlerRegisterHandlerMapping(val basePackages: Set<String>) : RequestMappingHandlerMapping() {

    private val config = RequestMappingInfo.BuilderConfiguration()

    override fun isHandler(beanType: Class<*>): Boolean {
        if (!isPackageInScope(beanType)) return false
        return AnnotatedElementUtils.hasAnnotation(beanType, ControllerFeignClient::class.java)
                && !beanType.isInterface
    }

    override fun getMappingForMethod(method: Method, handlerType: Class<*>): RequestMappingInfo? {
        var info = createRequestMappingInfo(method)
        if (info != null) {
            val typeInfo = createTypeRequestMappingInfo(handlerType)
            if (typeInfo != null) {
                info = typeInfo.combine(info)
            }
        }
        return info
    }

    private fun createRequestMappingInfo(element: AnnotatedElement): RequestMappingInfo? {
        val requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping::class.java)
        val condition = if (element is Class<*>) getCustomTypeCondition(element) else getCustomMethodCondition((element as Method))
        return requestMapping?.let { createRequestMappingInfo(it, condition) }
    }

    private fun  createTypeRequestMappingInfo(element: AnnotatedElement ): RequestMappingInfo? {
        val requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, ControllerFeignClient::class.java)
        val condition = if (element is Class<*>) getCustomTypeCondition(element) else getCustomMethodCondition((element as Method))
        return requestMapping?.let { createTypeRequestMappingInfo(it, condition) }
    }

    private fun createTypeRequestMappingInfo(
            feignClient: ControllerFeignClient, @Nullable customCondition: RequestCondition<*>?): RequestMappingInfo? {
        val builder = RequestMappingInfo
                .paths(*resolveEmbeddedValuesInPatterns(arrayOf(feignClient.path)))
                .methods(*feignClient.method)
                .params(*feignClient.params)
                .headers(*feignClient.headers)
                .consumes(*feignClient.consumes)
                .produces(*feignClient.produces)
                .mappingName(feignClient.name)
        if (customCondition != null) {
            builder.customCondition(customCondition)
        }
        return builder.options(this.config).build()
    }

    private fun isPackageInScope(beanType: Class<*>): Boolean {
        //是否在包路径内
        val packageName: String = ClassUtils.getPackageName(beanType)
        var isPackageScope = false
        for (basePackage in basePackages) {
            if (packageName.startsWith(basePackage)) {
                isPackageScope = true
                break
            }
        }
        return isPackageScope
    }


}